BaseEntity = function() {
    this.alive = true;
};

// Methods required by physics.
BaseEntity.prototype = {
    getPhyObj: function() {return this.phyObj;},
    kill: function() {this.alive = false;},
    update: function() {},
    think: function() {},
    respondGroundHit: function() {},
    setAcceleration: function() {}
};


Car = function(x, y, z, id) {
    if (!(this instanceof Car))
	return new Car(x, y, z, id);
    
    BaseEntity.apply(this);
    
    this.keys = 0;
    
    this.id = id;
    
    this.width = 5;
    this.height = 3;
    this.length = 10;
    
    var W = this.width; 
    var H = this.height; 
    var L = this.length;
    this.phyObj = PhyObj( [ Point(x - W/2, y - H/2, z - L/2),  // bottom
            			    Point(x - W/2, y - H/2, z + L/2),
            			    Point(x + W/2, y - H/2, z + L/2),
            			    Point(x + W/2, y - H/2, z - L/2),
            			    Point(x - W/2, y + H/2, z - L/2),  // top
            			    Point(x - W/2, y + H/2, z + L/2),
            			    Point(x + W/2, y + H/2, z + L/2),
            			    Point(x + W/2, y + H/2, z - L/2) ],
            			  [],
            			  []);
    // Add constraints for all pairs of points
    var points = this.phyObj.points;
    for (var i = 0; i < points.length; i++)
	for (var j = i+1; j < points.length; j++) {
	    var length = points[i].pos.sub(points[j].pos).length();
	    this.phyObj.constraints.push( Constraint(i, j, length ) );
	}

    this.upVec = Vec();
    this.forwardVec = Vec();
    this.rightVec = Vec();
    this.centerVec = Vec();
    this.update();
    this.haltedTicks = 0;
};
Car.prototype = Object.create(BaseEntity.prototype);

Car.prototype.getPhyObj = function() {
    return this.phyObj;
};

Car.prototype.update = function() {
    var points = this.phyObj.points;

    // update up/right/forward vectors
    this.upVec.set(   points[4].pos ).subMe( points[0].pos );
    this.upVec.addMe( points[6].pos ).subMe( points[2].pos );
    this.upVec.normalizeMe();

    this.rightVec.set(   points[0].pos ).subMe( points[3].pos );
    this.rightVec.addMe( points[1].pos ).subMe( points[2].pos );

    this.forwardVec = this.upVec.cross(this.rightVec);
    this.forwardVec.normalizeMe();
    this.rightVec = this.forwardVec.cross(this.upVec);

    // update center of mass
    this.centerVec.set(0,0,0);
    for (var i = 0; i < points.length; i++) {
	this.centerVec.addMe( points[i].pos );
    }
    this.centerVec.scaleMe(1/points.length);
};

Car.prototype.think = function() {
    var points = this.phyObj.points;
    
    if (points[4].groundContact &&
	points[5].groundContact &&
	points[6].groundContact &&
	points[7].groundContact) {
	this.haltedTicks++;
	if (this.haltedTicks > 40) {
	    // flip around
	    points[0].vel.set(0,0.575,0);
	    points[1].vel.set(0,0.575,0);
	    points[4].vel.set(0,0.575,0);
	    points[5].vel.set(0,0.575,0);
	}
    } else
	this.haltedTicks = 0;
};

Car.prototype.respondGroundHit = function(pointIndex, normal) {
    var point = this.phyObj.points[pointIndex];
    var vel = point.vel;
    var velNorm = vel.length();
    var reflectedVel = point.vel.sub( normal.scale(2*normal.dot(point.vel)) );
    var f = this.forwardVec;
    f = f.sub(normal.scale(f.dot(normal))); // forward parallel to ground
    f.normalizeMe();
    point.vel.set( f.scale(f.dot(reflectedVel)*.95) );

    // only front wheels may turn
    if ((this.keys & 0x8) && (pointIndex==1 || pointIndex==2)) 
	point.vel.addMe(this.rightVec.scale(point.vel.length()*0.5));
    if ((this.keys & 0x4) && (pointIndex==1 || pointIndex==2)) 
	point.vel.subMe(this.rightVec.scale(point.vel.length()*0.5));
    
};

Car.prototype.setAcceleration = function() {

    var gravity = Vec(0,-50,0);
    var engineAcc = Vec(0, 0, 0);

    if ((this.keys & 0x1) > 0) 
	engineAcc.set( this.forwardVec ).scaleMe(190);
    if ((this.keys & 0x2) > 0)
	engineAcc.set( this.forwardVec ).scaleMe(-80);

    var points = this.phyObj.points;
    for (var i = 0; i < points.length; i++) {
	var p = points[i];
	// Only wheels accelerate. 
	if (i<4 && p.groundContact)
	    p.acc.set( engineAcc );
	else
	    p.acc.set( gravity );
    }

};

Car.prototype.setKey = function(key, frameId) {
    var tmp = this.keys;
    this.keys |= key;
    console.log('Changed keys from ' + tmp + ' to ' + this.keys + ' in ' + frameId);
};

Car.prototype.unsetKey = function(key, frameId) {
    var tmp = this.keys;
    this.keys &= (~key);
    console.log('Changed keys from ' + tmp + ' to ' + this.keys + ' in ' + frameId);
};



SimpleBullet = function(x, y, z, dx, dy, dz) {
    BaseEntity.apply(this);
    this.health = 150;
    var p = Point(x, y, z);
    p.vel.set(dx, dy, dz).scaleMe(7);
    
    this.phyObj = PhyObj( [ p ],
			  [],
			  []);
    this.phyObj.moving = true;
}
SimpleBullet.prototype = Object.create(BaseEntity.prototype);

SimpleBullet.prototype.think = function() {
    this.health--;
    if (this.health < 0) {
	this.kill();
    }
};

SimpleBullet.prototype.respondGroundHit = function(pointIndex, normal) {
    this.health = 0;
};


Nitro = function(x, y, z) {
    this.health = 600;
    var pos = Point(x, y, z);
    this.phyObj = PhyObj( [ p ],
			  [],
			  []);    
};

Nitro.prototype.think = function() {
    this.health--;
    if (this.health < 0) {
	this.kill();
    }
};


